java中的String可以有多长?

您所在的位置:网站首页 js 循环字符串最小长度是多少 java中的String可以有多长?

java中的String可以有多长?

2024-06-27 04:58| 来源: 网络整理| 查看: 265

参考链接:     https://www.cnblogs.com/ibelieve618/p/6380328.html

https://www.cnblogs.com/htyj/p/8337209.html     https://blog.csdn.net/qq_36761831/article/details/99943600?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-0&spm=1001.2101.3001.4242

结论:

1、在编译期以字面量(字面量就是数据/数值,例如:1234,true,”abc”,’中’)形式放在java内存的栈中,以字节的个数代表String的长度,如果是拉丁字母,最长为65534(因为javac源码有个异常,不能为65535),如果含有中文(中文在utf-8中一般占三个字节),差别比较大,最大为65535。

2、存储的是数组和对象,凡是new建立的都是在堆中,理论上String最大长度为Integer.MAX_VALUE(2^31-1),但是有些VMs(虚拟机)需要保留一些空间给头信息,所以此时最大为Integer.MAX_VALUE-8,随意堆上的String的长度尽量小于Integer.MAX_VALUE-8。

 

概念:

栈内存:栈内存首先是一片内存区域,存储的都是局部变量,凡是定义在方法中的都是局部变量(方法外的是全局变量),for循环内部定义的也是局部变量,是先加载函数才能进行局部变量的定义,所以方法先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。栈内存的更新速度很快,因为局部变量的生命周期都很短。

 堆内存:存储的是数组和对象(其实数组就是对象),凡是new建立的都是在堆中,堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性),如果一个数据消失,这个实体也没有消失,还可以用,所以堆是不会随时释放的,但是栈不一样,栈里存放的都是单个变量,变量被释放了,那就没有了。堆里的实体虽然不会被释放,但是会被当成垃圾,Java有垃圾回收机制不定时的收取。

 

那么String在栈和堆中都是以什么样的形式存在呢?

例如:关于String str = "abc"的内部工作。 Java内部将此语句转化为以下几个步骤: (1)先定义一个名为str的对String类的对象引用变量:String str; (2)在栈中查找有没有存放值为"abc"的地址,如果没有,则开辟一个存放字面值为"abc"的地址,接着在栈中创建一个新的String类的对象o,并将o的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为"abc"的地址,则查找对象o,并返回o的地址。 (3)将str指向对象o的地址。 值得注意的是,一般String类中字符串值都是直接存值的。但像String str = "abc";这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用! 为了更好地说明这个问题,我们可以通过以下的几个代码进行验证。 String str1 = "abc"; String str2 = "abc"; System.out.println(str1==str2); //true 注意,我们这里并不用str1.equals(str2);的方式,因为这将比较两个字符串的值是否相等。==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,str1与str2是否都指向了同一个对象。 结果说明,JVM创建了两个引用str1和str2,但只创建了一个对象,而且两个引用都指向了这个对象。 我们再来更进一步,将以上代码改成: String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; System.out.println(str1 + "," + str2); //bcd, abc System.out.println(str1==str2); //false 这就是说,赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象。上例中,当我们将str1的值改为"bcd"时,JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。 事实上,String类被设计成为不可改变(final)的类。如果你要改变其值,可以,但JVM在运行时根据新值悄悄创建了一个新对象,然后将这个对象的地址返回给原来类的引用。这个创建过程虽说是完全自动进行的,但它毕竟占用了更多的时间。在对时间要求比较敏感的环境中,会带有一定的不良影响。 再修改原来代码: String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; String str3 = str1; System.out.println(str3); //bcd String str4 = "bcd"; System.out.println(str1 == str4); //true str3这个对象的引用直接指向str1所指向的对象(注意,str3并没有创建新对象)。当str1改完其值后,再创建一个String的引用str4,并指向因str1修改值而创建的新的对象。可以发现,这回str4也没有创建新的对象,从而再次实现栈中数据的共享。 我们再接着看以下的代码。   String str1 = new String("abc"); String str2 = "abc"; System.out.println(str1==str2); //false 创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。 String str1 = "abc"; String str2 = new String("abc"); System.out.println(str1==str2); //false 创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。 以上两段代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。 6. 数据类型包装类的值不可修改。不仅仅是String类的值不可修改,所有的数据类型包装类都不能更改其内部的值。 7. 结论与建议: (1)我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,我们创建了String类的对象str。担心陷阱!对象可能并没有被创建!唯一可以肯定的是,指向String类的引用被创建了。至于这个引用到底是否指向了一个新的对象,必须根据上下文来考虑,除非你通过new()方法来显要地创建一个新的对象。因此,更为准确的说法是,我们创建了一个指向String类的对象的引用变量str,这个对象引用变量指向了某个值为"abc"的String类。清醒地认识到这一点对排除程序中难以发现的bug是很有帮助的。 (2)使用String str = "abc";的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。 (3)当比较包装类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==。 (4)由于String类的final性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。

 

 

正文:如下图,两者的String长度最长都是多少呢?

栈内存中:

当我们使用字符串字面量直接定义String的时候,是会把字符串在常量池中存储一份的。那么上面提到的65534其实是常量池的限制。常量池中的每一种数据项也有自己的类型。Java中的UTF-8编码的Unicode字符串在常量池中以CONSTANT_Utf8类型表示。

我们使用字面量定义的字符串在字节码class文件中,是使用CONSTANTUtf8info存储的,而CONSTANTUtf8info中有u2 length;表明了该类型存储数据的长度。u2是无符号的16位整数,因此理论上允许的的最大长度是2^16=65536。所以u1的bytes理论上最多能存储65536个字节(下文解释其实不是65536)。其运行时加载到jvm内存的方法区中的常量池内,(如果常量池太小,longString放不下就会出现oom,常量池大小可以控制,一般不会出现常量池太小的问题)

    

但是在测试时发现,传入65535个a会报错Error:java:constant string too long,查看编译器源码(javac源码)发现如下代码,所以这是编译器源码的一个bug,对于拉丁字符(Latin)由于一个字符占用一个字节,就会触发下图的代码,导致最多能存储65534个字节。

如果存储的字符串含有中文,中文在utf-8中一般占据三个字节,所以不会触发上图的方法,而是先将中文转成utf-8的编码,如下图。此时的代码就是正确的,最多能存储65535个字节。

所以在栈内存中String的长度总结如下:

 

 

堆内存

new的String存放在堆内存中,在读取文件时,其实就是new 一个String,这个String的背后其实就是一个char的数组,这个数组的大小受虚拟机指令newArray的限制,这个newArray接受的是一个整型的参数,理论上最大为Integer.MAX_VALUE,但是有些VMs(虚拟机)需要保留一些空间给头信息,所以此时最大为Integer.MAX_VALUE-8,随意堆上的String的长度尽量小于Integer.MAX_VALUE-8。

 

总结:

 

 

本章回顾:

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3